﻿using FineUI;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SOA.Common;
using SOA.Model;
using SOA.Web.Cfg;
using SOA.Utils;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Threading.Tasks;
using System.Web;
using System.Web.Security;
using System.IO;
using System.Linq.Expressions;
using System.Diagnostics;
using System.Text;
using System.Collections.ObjectModel;
using NPOI.HSSF.UserModel;

namespace SOA.Web
{
    public class PageBase : System.Web.UI.Page
    {


        #region 多线程导出Excel

        /// <summary>
        /// 异步分页获取数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="S"></typeparam>
        /// <param name="pageIndexList">页码集合</param>
        /// <param name="pageSize">每页取多少条</param>
        /// <param name="DB">上下文对象</param>
        /// <param name="exprWhere">条件表达式</param>
        /// <param name="exprSort">排序表达式</param>
        /// <param name="asc">是否升序</param>
        /// <returns></returns>
        public List<T> GetPagedDataAsync<T, S>(List<int> pageIndexList, int pageSize, Expression<Func<T, bool>> exprWhere, Expression<Func<T, S>> exprSort, bool asc) where T : class
        {
            try
            {
                List<T> res = new List<T>();
                using (SOAContext DB = new SOAContext())
                {
                    foreach (int pageIndex in pageIndexList)
                    {
                        List<T> currentPageData = new List<T>();
                        int number = (pageIndex - 1) * pageSize;
                        if (asc)
                        {
                            currentPageData = DB.SetOrView<T>().Where(exprWhere).OrderBy(exprSort.Compile()).Skip(number).Take(pageSize).ToList();
                        }
                        else
                        {
                            currentPageData = DB.SetOrView<T>().Where(exprWhere).OrderByDescending(exprSort.Compile()).Skip(number).Take(pageSize).ToList();
                        }

                        res.AddRange(currentPageData);
                    }
                }

                return res;
            }
            catch (Exception ex)
            {
                File.WriteAllText(Server.MapPath("/Error" + Guid.NewGuid()), ex.ToString());
                return new List<T>();
            }

        }

        /// <summary>
        /// 异步导出Execl 最大支持导出百万级大数据、最大30个线程同时执行
        /// </summary>
        /// <param name="exprWhere"></param>
        /// <param name="exprSort"></param>
        /// <param name="asc"></param>
        /// <param name="totalCount"></param>
        /// <param name="fileName"></param>
        /// <param name="wait">主线程是否等待</param>
        /// <returns></returns>
        public Tuple<bool, string, string> ExportExeclAsync<T, S>(Expression<Func<T, bool>> exprWhere, Expression<Func<T, S>> exprSort, bool asc, int totalCount, string fileName, bool wait, Dictionary<string, string> customDic = null) where T : class
        {
            Tuple<bool, string, string> tuple;
            Stopwatch watch = new Stopwatch();
            watch.Start();

            fileName = CheckExt(fileName);

            int maxThreadNumber = 30;
            int exexlSheetMaxRowNumber = 65536;
            int pageSize = 5000;
            int totalPageCount = (totalCount - 1) / pageSize + 1; //总页数 

            //这里已考虑了sheet溢出
            int threadCount = GetThreadCount(exexlSheetMaxRowNumber, totalCount);

            if (threadCount >= maxThreadNumber)
            {
                tuple = new Tuple<bool, string, string>(false, "当前查询到" + totalCount + "条数据，过于庞大，请增加过滤条件", "");
                return tuple;
            }

            //绝对地址
            string execlPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "execl\\" + fileName);
            if (!Directory.Exists(Path.GetDirectoryName(execlPath)))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(execlPath));
            }

            List<List<T>> superList = new List<List<T>>();
            Action actHr = () =>
            {
                List<IAsyncResult> workerAsyncResultList = new List<IAsyncResult>();

                List<List<int>> SuperPageIndexList = CreateSuperPageIndexList(totalPageCount, threadCount);

                for (int i = 0; i < threadCount; i++)
                {
                    List<int> pageIndexList = SuperPageIndexList[i];

                    Func<List<T>> actWorker = new Func<List<T>>(() => this.GetPagedDataAsync<T, S>(pageIndexList, pageSize, exprWhere, exprSort, asc));
                    IAsyncResult workerAsyncResult = actWorker.BeginInvoke(t =>
                    {
                        //回调
                        List<T> dataList = actWorker.EndInvoke(t);
                        superList.Add(dataList);
                    }, null);

                    workerAsyncResultList.Add(workerAsyncResult);
                }

                //hr等待所有worker 子线程完成,这段注释了就不等待了
                foreach (IAsyncResult item in workerAsyncResultList)
                {
                    //主线程等待当前item线程，用循环，就等待所有线程
                    item.AsyncWaitHandle.WaitOne();
                }
            };

            //主线程(boss)等待hr汇报
            if (wait)
            {
                IAsyncResult hrAsyncResult = actHr.BeginInvoke(null, null);
                actHr.EndInvoke(hrAsyncResult);
                SaveExecl(execlPath, superList, exprSort, asc, customDic);
                superList = null; //早点GC
                FileInfo originalFile = new FileInfo(execlPath);
                FileInfo compressedFile = originalFile.Compress();
                if (compressedFile != null)
                {
                    System.IO.File.Delete(execlPath);
                }
            }
            else
            {
                IAsyncResult hrAsyncResult = actHr.BeginInvoke(m =>
                {
                    SaveExecl(execlPath, superList, exprSort, asc, customDic);
                    superList = null;
                    FileInfo originalFile = new FileInfo(execlPath);
                    FileInfo compressedFile = originalFile.Compress();
                    if (compressedFile != null)
                    {
                        System.IO.File.Delete(execlPath);
                    }
                }, null);
            }

            watch.Stop();
            string jiShiPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "execl\\jishi.txt");
            System.IO.File.AppendAllText(jiShiPath, watch.ElapsedMilliseconds.ToString() + "\r\n===========\r\n", Encoding.Unicode);
            string compressedFilePath = execlPath + ".gz";
            tuple = new Tuple<bool, string, string>(true, "成功导出" + totalCount + "条数据", compressedFilePath);
            return tuple;

        }


        /// <summary>
        /// 检查后缀
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private static string CheckExt(string fileName)
        {
            string ext = Path.GetExtension(fileName);
            if (ext != ".xls")
            {
                int index = fileName.LastIndexOf(".");
                if (index > 0)
                {
                    fileName = fileName.Substring(0, index) + ".xls";
                }
                else
                {
                    fileName = fileName + ".xls";
                }
            }

            return fileName;
        }

        /// <summary>
        /// 获取线程数量
        /// </summary>
        /// <param name="exexlSheetMaxRowNumber">exexlSheet最大行数</param>
        /// <param name="totalCount">总记录数</param>
        /// <returns></returns>
        private static int GetThreadCount(int exexlSheetMaxRowNumber, int totalCount)
        {
            int threadCount = totalCount / exexlSheetMaxRowNumber + 1;
            return threadCount;
        }

        /// <summary>
        /// 获取超级页码集合 ---  里面有个均分并且有序的小算法
        /// </summary>
        /// <param name="totalPageCount"></param>
        /// <param name="threadCount"></param>
        /// <returns></returns>
        private List<List<int>> CreateSuperPageIndexList(int totalPageCount, int threadCount)
        {
            List<List<int>> super = new List<List<int>>();
            int remainder = 0; //余数
            //商
            int quotient = Math.DivRem(totalPageCount, threadCount, out remainder);
            var list = Enumerable.Range(1, totalPageCount).ToList();
            for (int i = 0; i < threadCount; i++)
            {
                if (i < remainder)
                {
                    super.Add(list.Take(quotient + 1).ToList());
                    list.RemoveRange(0, quotient + 1);
                }
                else
                {
                    super.Add(list.Take(quotient).ToList());
                    list.RemoveRange(0, quotient);
                }
            }

            return super;
        }

        /// <summary>
        /// 保存execl
        /// </summary>
        /// <param name="execlPath"></param>
        /// <param name="superList"></param>
        private static string SaveExecl<T, S>(string execlPath, List<List<T>> superList, Expression<Func<T, S>> exprSort, bool asc, Dictionary<string, string> customDic = null) where T : class
        {
            #region 伪排序算法
            ReadOnlyCollection<ParameterExpression> _params = exprSort.Parameters;
            string filed = _params[0].Name;


            int index = 0;
            Dictionary<object, int> dit = new Dictionary<object, int>();
            foreach (List<T> item in superList)
            {
                if (item.Count == 0)
                {
                    continue;
                }
                PropertyInfo prop = item[0].GetType().GetProperty(filed);
                object oValue = prop.GetValue(item[0]);
                dit.Add(oValue, index);
                index++;
            }

            IOrderedEnumerable<KeyValuePair<object, int>> keyValuePair;
            if (asc)
            {
                keyValuePair = dit.OrderBy(m => m.Key);
            }
            else
            {
                keyValuePair = dit.OrderByDescending(m => m.Key);
            }


            #endregion

            //合并数据
            HSSFWorkbook wk = new HSSFWorkbook();
            int j = 1;
            foreach (KeyValuePair<object, int> item in keyValuePair)
            {
                int i = item.Value;
                List<T> sheetData = superList[i];
                wk.CreateSheet("Sheet" + j).CreateHeader<T>(customDic).FillSheet<T>(sheetData, customDic);
                j++;
            }

            using (FileStream fs = new FileStream(execlPath, FileMode.Create, FileAccess.Write))
            {
                wk.Write(fs);//保存文件
            };

            return execlPath;
        }

        /// <summary>
        /// 响应文件流
        /// </summary>
        /// <param name="srcPath"></param>
        /// <param name="srcFileName"></param>
        public void ResponseFileStream(string srcPath, string srcFileName)
        {
            Response.AddHeader("Content-Disposition", "attachment; filename=" + srcFileName);
            using (FileStream srcReader = new FileStream(Path.Combine(srcPath, srcFileName), FileMode.Open, FileAccess.Read))
            {
                int readCount = 0;
                int bufferSize = 1024 * 4 * 1024;
                byte[] readBuffer = new byte[bufferSize];
                while ((readCount = srcReader.Read(readBuffer, 0, bufferSize)) > 0)
                {
                    Response.OutputStream.Write(readBuffer, 0, readCount);
                    Response.Flush();
                }
            }
        }

        #endregion

        
    }
}